﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

// 预处理解析

// 预定义符号
// c语言中设置一些预定的符号，我们可以直接使用

// 列：
//__FILE__ //进⾏编译的源⽂件
//__LINE__ //⽂件当前的⾏号
//__DATE__ //⽂件被编译的⽇期
//__TIME__ //⽂件被编译的时间
//__STDC__ //如果编译器遵循ANSI C，其值为1，否则未定义

#if 0
int main()
{
	printf("file = %s\n", __FILE__); //文件地址
	printf("line = %d\n", __LINE__); //行号
	printf("date = %s\n", __DATE__); //编译日期
	printf("time = %s\n", __TIME__); //编译时间
	return 0;
}
#endif

//  - / 续行符号
#if 0
int main()
{
	printf("file = %s\nline = %d\ndate = %s\ntime = %s\n", \
		__FILE__, __LINE__, \
		__DATE__, __TIME__);

	return 0;
}
#endif


//#define CASE break;case //在写case语句的时候⾃动把 break写上。
//
//int main()
//{
//	int input = 0;
//	scanf("%d", &input);
//	switch(input)
//	{
//	case 1:
//		printf("%d\n", input);
//
//	case 2:
//		printf("%d\n", input);
//		
//	case 3:
//		printf("%d\n", input);
//		
//	default:
//		printf("err\n");
//		break;
//	}
//	return 0;
//}

// #define 用法

// 定义常量
//#define max 100
//int main()
//{
//	printf("%d\n", max);
//	return 0;
//}

// 给关键字创建一个别的名称
//#define i int
//i main()
//{
//	i a = 10;
//	printf("%d\n", a);
//	return 0;
//}

// 我们平时使用代码时结尾都是以;，那么#define后面需不需要加上呢

//#define min 1;
//#define max 1
//int main()
//{
//	printf("%d\n", min); //err
//	return 0;
//}

// 答案是不能因为在预处理阶段，编译器会删除所有的#define，并且替换定义的变量
// 拿上面的代码举例
// 下面是预处理替换后的代码
//int main()
//{
//	printf("%d\n", 1;);
//	printf("%d\n", 1);
//	return 0;
//}

// #define 定义宏 - 可以传入参数

// 比如： 
//#define add(x) x * x
//int main()
//{
//	printf("%d\n", add(2 + 1));
//  return 0;
//}
// 这段代码会输出多少？有木有小伙伴算的是9，毕竟3*3是9，那么这样就错了
// 下面我们一起来看看

// 首先我们前面了解到#define的内容会直接被替换
// 替换后
//int main()
//{
//	printf("%d\n", 2 + 1 * 2 + 1);
//	return 0;
//}
// 根据优先级， 2+（1*2）+1 == 5
// 这种直接的替换往往也会带来一些小麻烦，要想解决也很容易，只要勤加（）就行了
#if 0
#define add(x) (x)*(x)
int main()
{
	printf("%d\n", add(2 + 1));
	return 0;
}
#endif
// 这样就不用担心优先级带来的麻烦了

// 这样就真的改好了吗，我们来看下面一段代码
#if 0
#define add(x) (x)+(x)
int main()
{
	printf("%d\n", 10 * add(5)); //我们想要的结果是100
	return 0;
}
#endif
// 输出的确实55，这还是犯了与上面同样的问题，我们的（）加少了
// 最好的方案是在外面一层也加上
#if 0
#define add(x) ((x)+(x))
int main()
{
	printf("%d\n", 10 * add(5));
	return 0;
}
#endif

// 带有副作用的宏参数

// 举例：

//#define MAX(a, b) ((a) > (b) ? (a) : (b))
#if 0
int main()
{
	int a = 3;
	int b = 10;
	int z = MAX(a++, b++);
	printf("%d\n %d\n %d\n", a, b, z);
	return 0;
}
#endif
// 由上面的函数我们来进行替换
//int main()
//{
//	int a = 3;
//	int b = 10;
//	int z = ((a++) > (b++) ? (a++) : (b++));
//	// 先执行(a++)>(b++)  -  a=4, b=11;
//	// 因为a<b, 所以执行表达式2(b++) - a=4,z=11,b=12
//	printf("%d\n%d\n%d\n", a, z, b);
//	return 0;
//}
// 这就是宏参数的副作用，如果换成函数又如何呢？
//int fution(int a, int b)
//{
//	return a > b ? a : b;
//}
//
//int main()
//{
//	int a = 3;
//	int b = 10;
//	int z = fution(a++, b++);
//	printf("%d\n%d\n%d\n", a, z, b);
//	return 0;
//}
// 我们可以看见z的结果是10，而a，b的结果分别加了1，函数在传参后函数中a与b不再改变。


// 宏替换的规则
//1.在调⽤宏时，⾸先对参数进⾏检查，看看是否包含任何由#define定义的符号。如果是，它们⾸先
//被替换。
//2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏，参数名被他们的值所替换。
//3. 最后，再次对结果⽂件进⾏扫描，看看它是否包含任何由#define定义的符号。如果是，就重复上
//述处理过程。

//注意：
//1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏，不能出现递归。
//2. 当预处理器搜索#define定义的符号的时候，字符串常量的内容并不被搜索。


// 宏函数的对比

// 宏函数常常应用于比较简单的运算

// 如：比较大小

// 宏写
#if 0
#define MAX(a, b)  ((a)>(b)?(a):(b))
int main()
{
	printf("%d\n", MAX(2, 3));
	return 0;
}
#endif
// 函数写
#if 0
int MAX(int a, int b)
{
	return a > b ? a : b;
}
int main()
{
	printf("%d\n", MAX(2, 3));
	return 0;
}
#endif

// 为什么使用宏来运算而不使用函数
// 1. 相比较函数的调用，直接替换的计算工作时间更快
// 2. 宏与类型无关，你可以比较任意类型，然而函数却需要更改返回类型

// 宏函数的劣势
// 1.使用宏的时候，就相当于插入一段定义的代码，如果宏过长会导致代码太多，不好观察
// 2.宏无法调试
// 3.宏可能会带来优先级的问题，与类型也无关，不够严谨


// #和##
// #运算符
// #运算符所执行的操作可以将宏中的一个参数转换为字符串字面量

//如：我们有一个变量 int a = 20;
//我们想要使用宏然后打印出 the num of a is 20.
#if 0
#define PRINT(n) printf("the num of "#n " is %d",n);
int main()
{
	int a = 20;
	PRINT(a);
	return 0;
}
#endif
//当我们按照下⾯的⽅式调⽤的时候：
//PRINT(a);//当我们把a替换到宏的体内时，就出现了#a，⽽#a就是转换为"a"，这时⼀个字符串
//代码就会被预处理为：
//printf("the num of "a " is %d",a);

// ##运算符
#if 0
#define FUNTION_MAX(type) \
type type##_max(type a, tpye b)\
{                              \
      return (a > b ? a : b); \
}
#endif
// 这样使用后我们可以传入不同类型的参数了


// 命名约定
// 我们一般宏的使用命名时都全部大写，对于函数的应用却不全部大写


// #undef
// 这条命令可以移除一个宏定义
#if 0
#define MAX 100
int main()
{
	printf("%d\n", MAX);
#undef MAX
	printf("%d\n", MAX); //err
	return 0;
}
#endif

// 条件编译
// 有时我们需要用一段代码，但用后后面可能还要使用，我们希望保留，并且设定一个开关，这就是条件编译
#if 0
#define __DEBUG__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i + 1;
#ifdef __DEBUG__
		printf("%d ", arr[i]);
#endif
	}
	return 0;
}
#endif
// 这样之后我们只有在前面定义 __DEBUG__ 我们才可以使用中间的代码

